/**
 * @(#)AbstractDao.java, 2018/2/2.
 * <p/>
 * Copyright 2018 Kerr Wang, Inc. All rights reserved.
 * KERRWANG PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package org.wangzx.exercise.dao;

import com.alibaba.fastjson.JSON;
import com.mysql.jdbc.Statement;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.wangzx.exercise.enums.EnumIntegerInterface;
import org.wangzx.exercise.exception.DBException;
import org.wangzx.exercise.exception.DuplicateKeyException;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author hzwangzuxiang(ahscwzx@163.com)
 */
public abstract class AbstractDao<T> {
    private static Logger logger = LoggerFactory.getLogger(AbstractDao.class);

    @Autowired
    protected SqlSessionFactory sqlSessionFactory;

    @Autowired
    protected SqlSessionTemplate sqlSessionTemplate;

    protected final String classStr = this.getClass().getName();

    protected String tableName = "";

    /**
     * 一次性批量操作的数量限制
     */
    private static final int BATCH_SIZE_LIMIT = 200;

    @PostConstruct
    public void init() {
        tableName = StringUtils
            .strip(sqlSessionTemplate.getConfiguration().getSqlFragments().get(classStr + ".table").getStringBody());
    }

    public SqlSessionFactory getSqlSessionFactory() {
        return sqlSessionFactory;
    }

    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

    protected SqlSession getSqlSession() {
        return sqlSessionTemplate;
    }

    protected SqlSession getSqlSession(boolean autoCommit) {
        return sqlSessionFactory.openSession(autoCommit);
    }

    protected SqlSession getSqlSession(ExecutorType execType, boolean autoCommit) {
        return sqlSessionFactory.openSession(execType, autoCommit);
    }

    protected SqlSession getBatchSqlSession() {
        return sqlSessionFactory.openSession(ExecutorType.BATCH, false);
    }

    /**
     * 将一个po对象插入到对应的数据库，并返回该条记录的id
     *
     * @param obj
     * @throws SQLException
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public long insert(T obj) {
        logger.info("[op:insert] insert object, obj={}", JSON.toJSONString(obj));
        Class clazz = obj.getClass();
        List<Field> fieldList = new ArrayList<Field>();
        // 组装sql语句
        StringBuilder sqlBuilder = new StringBuilder();
        try {
            // 通过反射获取所有的字段，并插入到数据库中去
            Field[] fields = clazz.getDeclaredFields();
            if (fields == null || fields.length == 0) {
                // 不合法的情况
            }

            sqlBuilder.append("insert into ").append(this.tableName).append(" (");
            for (Field field: fields) {
                field.setAccessible(true);
                if (!field.getName().equals("id")) {
                    fieldList.add(field);
                    sqlBuilder.append(field.getName()).append(",");
                }
            }
            // 去除最后一个逗号
            sqlBuilder.deleteCharAt(sqlBuilder.length() - 1);
            sqlBuilder.append(") values (");
            for (Field field1: fields) {
                if (!field1.getName().equals("id")) {
                    sqlBuilder.append("?").append(",");
                }
            }
            // 去除最后一个逗号
            sqlBuilder.deleteCharAt(sqlBuilder.length() - 1);
            sqlBuilder.append(")");
            SqlSession session = sqlSessionFactory.openSession(true);
            Connection conn = session.getConnection();
            logger.debug("[op:insert] insert sql string, string={}", sqlBuilder.toString());
            PreparedStatement preparedStatement = conn.prepareStatement(sqlBuilder.toString(),
                Statement.RETURN_GENERATED_KEYS);


            try {
                // 设置参数
                for (int i = 0; i < fields.length - 1; i++) {
                    Object field = fieldList.get(i).get(obj);
                    if (field instanceof EnumIntegerInterface) {
                        preparedStatement.setObject(i + 1, ((EnumIntegerInterface) field).getValue());
                    } else {
                        preparedStatement.setObject(i + 1, field);
                    }
                }
                preparedStatement.executeUpdate();
                long autoIncKey = -1;
                ResultSet rs = preparedStatement.getGeneratedKeys();
                if (rs.next()) {
                    autoIncKey = rs.getLong(1);
                } else {
                    throw new DBException("get id error");
                }
                return autoIncKey;
            } finally {
                session.close();
            }
        } catch (Exception e) {
            if (e.getMessage().endsWith("Duplicate record.")) { // 命中了唯一键的限制
                logger.error("inert error,,tableName={},sqlString={}, errorCode={}", tableName, sqlBuilder.toString(),
                    e.getMessage(), e);
                throw new DuplicateKeyException(e);
            }
            // 中间发生了异常，打印堆栈
            logger.error("inert error,tableName={},sqlString={}, errorCode={}", tableName, sqlBuilder.toString(),
                e.getMessage(), e);
            throw new DBException(e);
        }
    }
}
